#include "Game.hpp"
#include "Display.hpp"
#include "Input/InputMapper.h"
#include "GameOver.hpp"
#include "Render/Camera.hpp"
#include "boost/shared_ptr.hpp"

// Evil globals
double gAxisX = 0;
double gAxisY = 0;
int gLastX = 0;
int gLastY = 0;
bool gStateOne = false;
bool gStateTwo = false;
bool gStateThree = false;

void InputCallback( InputMapping::MappedInput& inputs );

namespace
{
	// Helper for converting VK codes into raw input button codes
	bool ConvertWParamToRawButton(WPARAM wparam, InputMapping::RawInputButton& button)
	{
		switch(wparam)
		{
		case 0x30:		button = InputMapping::RAW_INPUT_BUTTON_ZERO;	break;
		case 0x31:		button = InputMapping::RAW_INPUT_BUTTON_ONE;	     break;
		case 0x32:		button = InputMapping::RAW_INPUT_BUTTON_TWO;	     break;
		case 0x33:	     button = InputMapping::RAW_INPUT_BUTTON_THREE;	break;
		case 0x34:		button = InputMapping::RAW_INPUT_BUTTON_FOUR;	break;
		case 0x35:		button = InputMapping::RAW_INPUT_BUTTON_FIVE;	break;
		case 0x36:		button = InputMapping::RAW_INPUT_BUTTON_SIX;	     break;
		case 0x37:		button = InputMapping::RAW_INPUT_BUTTON_SEVEN;	break;
		case 0x38:	     button = InputMapping::RAW_INPUT_BUTTON_EIGHT;	break;
		case VK_ESCAPE:     button = InputMapping::RAW_INPUT_BUTTON_NINE;	break;
		default:		     return false;								break;
		}

		return true;
	}
}

inline LRESULT CALLBACK WndProc( HWND _wnd, UINT _msg, WPARAM _wparam, LPARAM _lparam )
{
     switch( _msg )
     {
     case WM_PAINT:
          ValidateRect( _wnd, NULL );
          break;

     case WM_MBUTTONDOWN:
          {
               SetCapture( _wnd );
			InputMapping::RawInputButton button;
			if( ConvertWParamToRawButton( 0x33, button ) )
				InputMapping::InputMapper::GetInstance().SetRawButtonState( button, true, true );
          }
          break;

     case WM_MBUTTONUP:
          {
               ReleaseCapture();
			InputMapping::RawInputButton button;
			if( ConvertWParamToRawButton( 0x33, button ) )
				InputMapping::InputMapper::GetInstance().SetRawButtonState( button, false, true );
          }
          break;

     case WM_MOUSEMOVE:
          {
               int x = LOWORD(_lparam);
               int y = HIWORD(_lparam);

               InputMapping::InputMapper::GetInstance().SetRawAxisValue(   InputMapping::RAW_INPUT_AXIS_MOUSE_X, 
                                                                           static_cast<double>(x - gLastX));
               InputMapping::InputMapper::GetInstance().SetRawAxisValue(   InputMapping::RAW_INPUT_AXIS_MOUSE_Y, 
                                                                           static_cast<double>(y - gLastY));

               gLastX = x;
               gLastY = y;
          }
          break;

	case WM_KEYDOWN:
		{
			InputMapping::RawInputButton button;
			bool previouslyDown = ((_lparam & (1 << 31)) != 0);

			if(ConvertWParamToRawButton(_wparam, button))
				InputMapping::InputMapper::GetInstance().SetRawButtonState( button, true, previouslyDown );
		}
		break;

	case WM_KEYUP:
		{
			InputMapping::RawInputButton button;
			if(ConvertWParamToRawButton(_wparam, button))
				InputMapping::InputMapper::GetInstance().SetRawButtonState( button, false, true );
		}
		break;

     case WM_CLOSE:
          GameOver::GetInstance().SetGameOver();
          break;

     default:
          return DefWindowProc( _wnd, _msg, _wparam, _lparam );
     }

    return 0;
}

int APIENTRY WinMain( HINSTANCE _instance, HINSTANCE _prev_instance, LPSTR _cmd_line_ptr, int _cmd_show )
{
     const char window_title[] = "Toffee";

     HINSTANCE instance = GetModuleHandle(0);

     WNDCLASSEX windowClass;
     windowClass.cbSize = sizeof( WNDCLASSEX );
     windowClass.style = CS_OWNDC;
     windowClass.lpfnWndProc = &WndProc;
     windowClass.cbClsExtra = 0;
     windowClass.cbWndExtra = 0;
     windowClass.hInstance = instance;
     windowClass.hIcon = 0;
     windowClass.hCursor = LoadCursor( NULL, IDC_ARROW );
     windowClass.hbrBackground = NULL;
     windowClass.lpszMenuName = NULL;
     windowClass.lpszClassName = window_title;
     windowClass.hIconSm = NULL;

     UnregisterClass( window_title, instance );

     if( !RegisterClassEx( &windowClass ) ) 
          return false;

     // create window
     RECT rect;
     rect.left = 0;
     rect.top = 0;
     rect.right = DISPLAY_WIDTH;
     rect.bottom = DISPLAY_HEIGHT;
     AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, 0 );
     rect.right -= rect.left;
     rect.bottom -= rect.top;

     int x = ( GetSystemMetrics(SM_CXSCREEN) - rect.right ) >> 1;
     int y = ( GetSystemMetrics(SM_CYSCREEN) - rect.bottom ) >> 1;

     HWND window = CreateWindow( window_title, window_title, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME, 
                                 x, y, rect.right, rect.bottom,
                                 NULL, NULL, instance, NULL );

     if( window )
     {
          // Setup the display
          boost::shared_ptr< Display > displayPtr( new Display() );
          displayPtr->SetDisplayWindow( window );
          displayPtr->OpenDisplay();

     	// Configure input
     	InputMapping::InputMapper::GetInstance().PushContext(L"maincontext");
     	InputMapping::InputMapper::GetInstance().AddCallback(InputCallback, 0);

          // Game loop
     	Game game(displayPtr);
     	game.StartGame();
     	game.UpdateGame();
     	game.StopGame();

          // Close display before closing application
     	displayPtr->CloseDisplay();
     }

     return 0;
}

void InputCallback( InputMapping::MappedInput& inputs )
{
	gStateOne = inputs.States.find(InputMapping::STATE_ONE) != inputs.States.end();
	gStateTwo = inputs.States.find(InputMapping::STATE_TWO) != inputs.States.end();
	gStateThree = inputs.States.find(InputMapping::STATE_THREE) != inputs.States.end();

     if( gStateThree )
     {
          Camera::GetInstance().RotateX( inputs.Ranges[InputMapping::RANGE_ONE] );
     	Camera::GetInstance().RotateY( inputs.Ranges[InputMapping::RANGE_TWO] );
     }

	//if(inputs.Actions.find(InputMapping::ACTION_ONE) != inputs.Actions.end())

	//if(inputs.Actions.find(InputMapping::ACTION_TWO) != inputs.Actions.end())
	//	PushLogLine(L"Action 2 fired!");

	//if(inputs.Actions.find(InputMapping::ACTION_THREE) != inputs.Actions.end())
	//	PushLogLine(L"Action 3 fired!");

	//if(inputs.Actions.find(InputMapping::ACTION_FOUR) != inputs.Actions.end())
	//	PushLogLine(L"Action 4 fired!");

	//if(inputs.Actions.find(InputMapping::ACTION_FIVE) != inputs.Actions.end())
		//PushLogLine(L"Action 5 fired!");

     if(inputs.Actions.find(InputMapping::ACTION_SIX) != inputs.Actions.end())
          GameOver::GetInstance().SetGameOver();

	//if(inputs.Actions.find(InputMapping::ACTION_SEVEN) != inputs.Actions.end())
	//	PushLogLine(L"Action 7 fired!");
}